<!DOCTYPE html>
<html class="has-navbar-fixed-top">
<head>
    <meta charset="utf-8">
<title>XML与JSON - wanzixin</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/outdated-browser/1.1.5/outdatedbrowser.min.css">


<link href="/zh-cn/Study/Java/XML%E4%B8%8EJSON/" rel="alternate" hreflang="zh-CN" />
    


<meta name="description" content="">





    <meta name="description" content="XML和JSON是两种经常在网络使用的数据格式。本章我们介绍如何使用Java读写XML和JSON。">
<meta property="og:type" content="article">
<meta property="og:title" content="XML与JSON">
<meta property="og:url" content="https://wanzixin.github.io/Study/Java/XML%E4%B8%8EJSON/index.html">
<meta property="og:site_name" content="wanzixin">
<meta property="og:description" content="XML和JSON是两种经常在网络使用的数据格式。本章我们介绍如何使用Java读写XML和JSON。">
<meta property="og:locale" content="en_US">
<meta property="article:published_time" content="2021-05-31T02:32:16.000Z">
<meta property="article:modified_time" content="2021-06-01T03:07:55.576Z">
<meta property="article:author" content="wanzixin">
<meta name="twitter:card" content="summary">





<link rel="icon" href="/favicon.png">


<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Ovo|Source+Code+Pro">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">


<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/lightgallery/1.6.8/css/lightgallery.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/justifiedGallery/3.6.5/css/justifiedGallery.min.css">


<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/atom-one-light.min.css">


<link rel="stylesheet" href="/css/style.css">


<script defer src="//use.fontawesome.com/releases/v5.0.8/js/all.js"></script>


    
    
    
    
    
    
    
    
    
    

    


<meta name="generator" content="Hexo 5.4.0"></head>
<body>
    
<nav class="navbar is-transparent is-fixed-top navbar-main" role="navigation" aria-label="main navigation">
    <div class="container">
        <div class="navbar-brand">
            <a class="navbar-item navbar-logo" href="/">
                
                    
                    wanzixin
                    
                
            </a>
            <div class="navbar-burger">
                <span></span>
                <span></span>
                <span></span>
            </div>
        </div>
        
        <div class="navbar-menu navbar-start">
            
            <a class="navbar-item "
               href="/archives">Archives</a>
            
            <a class="navbar-item "
               href="/categories">Categories</a>
            
            <a class="navbar-item "
               href="/categories/Diary">Diary</a>
            
            <a class="navbar-item "
               href="/categories/Gallery">Gallery</a>
            
            <a class="navbar-item "
               href="/categories/Study">Study</a>
            
            <a class="navbar-item "
               href="/categories/Item">Item</a>
            
            <a class="navbar-item "
               href="/about">About</a>
            
        </div>
        
        <div class="navbar-menu navbar-end">
            
            <a class="navbar-item search" title="Search" href="javascript:;">
                <i class="fas fa-search"></i>
            </a>
            
            
            <div class="navbar-item is-hoverable has-dropdown is-hidden-mobile is-hidden-tablet-only toc">
                <a class="navbar-item" title="Table of Contents">
                    <i class="fa fa-list"></i>
                </a>
                <div class="navbar-dropdown is-right">
                    
                    
                    
                    
                    <a class="navbar-item" href="#XML简介">1&nbsp;&nbsp;<b>XML简介</b></a>
                    
                    
                    
                    <a class="navbar-item" href="#XML的结构">1.1&nbsp;&nbsp;XML的结构</a>
                    
                    
                    <hr class="navbar-divider">
                    
                    
                    <a class="navbar-item" href="#使用DOM">2&nbsp;&nbsp;<b>使用DOM</b></a>
                    
                    
                    
                    <a class="navbar-item" href="#小结">2.1&nbsp;&nbsp;小结</a>
                    
                    
                    <hr class="navbar-divider">
                    
                    
                    <a class="navbar-item" href="#使用SAX">3&nbsp;&nbsp;<b>使用SAX</b></a>
                    
                    
                    <hr class="navbar-divider">
                    
                    
                    <a class="navbar-item" href="#使用Jackson">4&nbsp;&nbsp;<b>使用Jackson</b></a>
                    
                    
                    <hr class="navbar-divider">
                    
                    
                    <a class="navbar-item" href="#使用JSON">5&nbsp;&nbsp;<b>使用JSON</b></a>
                    
                </div>
            </div>
            
            
            <a class="navbar-item" title="GitHub" target="_blank" rel="noopener" href="https://github.com/wanzixin">
                
                <i class="fab fa-github"></i>
                
            </a>
               
            
        </div>
    </div>
</nav>

    <section class="section">
    <div class="container">
    <article class="article content gallery" itemscope itemprop="blogPost">
    <h1 class="article-title is-size-3 is-size-4-mobile" itemprop="name">
        
            XML与JSON
        
    </h1>
    <div class="article-meta columns is-variable is-1 is-multiline is-mobile is-size-7-mobile">
        <span class="column is-narrow">
            
                <span>May 31 2021</span>
            
        </span>
        
        <span class="column is-narrow article-category">
            <i class="far fa-folder"></i>
            <a class="article-category-link" href="/categories/Study/">Study</a><span>></span><a class="article-category-link" href="/categories/Study/Java/">Java</a>
        </span>
        
        
        <span class="column is-narrow">
            
            
            24 minutes read (About 3649 words)
        </span>
        
    </div>
    <div class="article-entry is-size-6-mobile" itemprop="articleBody">
    
        <html><head></head><body><p>XML和JSON是两种经常在网络使用的数据格式。本章我们介绍如何使用Java读写XML和JSON。<span id="more"></span></p>
<h2 id="XML简介"><a href="#XML简介" class="headerlink" title="XML简介"></a>XML简介</h2><p>XML是可扩展标记语言（eXtensible Markup Language）的缩写，它是一种数据表示格式，可以描述非常复杂的数据结构，常用于传输和存储数据。</p>
<p>XML有几个特点：一是纯文本，默认使用UTF-8编码；二是可嵌套，适合表示结构化数据。如果把XML内容存为文件，那它就是一个XML文件。此外，XML内容经常通过网络进行消息传输。</p>
<h3 id="XML的结构"><a href="#XML的结构" class="headerlink" title="XML的结构"></a>XML的结构</h3><p>XML有固定的结构，首行必定是<code>&lt;?xml version="1.0"?&gt;</code>，可以加上可选编码。紧接着，如果以类似<code>&lt;!DOCTYPE note SYSTEM "book.dtd"&gt;</code>声明的是文档定义类型（DTD：Document Type Definition），DTD是可选的。接下来是XML的文档内容，一个XML文档有且仅有一个根元素，根元素可以包含任意个子元素，元素可以包含属性，例如，<code>&lt;isbn lang="CN"&gt;1234567&lt;/isbn&gt;</code>包含一个属性<code>lang="CN"</code>，且元素必须正确嵌套。如果是空元素，可以用<code>&lt;tag/&gt;</code>表示。</p>
<p>由于使用了<code>&lt;</code>，<code>&gt;</code>和引号等标识符，如果内容出现了特殊符号，需要使用<code>&amp;???;</code>表示转义。例如，<code>Java&lt;tm&gt;</code>必须写成：</p>
<figure class="highlight plaintext hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">&lt;name&gt;Java&amp;lt;tm&amp;gt;&lt;/name&gt;<br></code></pre></td></tr></tbody></table></figure>

<p>常见的特殊字符如下：</p>
<table>
<thead>
<tr>
<th align="center">字符</th>
<th align="center">表示</th>
</tr>
</thead>
<tbody><tr>
<td align="center">&lt;</td>
<td align="center">&lt;</td>
</tr>
<tr>
<td align="center">&gt;</td>
<td align="center">&gt;</td>
</tr>
<tr>
<td align="center">&amp;</td>
<td align="center">&amp;</td>
</tr>
<tr>
<td align="center">“</td>
<td align="center">"</td>
</tr>
<tr>
<td align="center">‘</td>
<td align="center">'</td>
</tr>
</tbody></table>
<p>格式正确的XML（Well Formed）是指XML的格式是正确的，可以被解析器正确读取。而合法的XML是指，不但XML格式正确，而且它的数据结构可以被DTD或者XSD验证。</p>
<p>DTD文档可以指定一系列规则，例如：</p>
<ul>
<li>根元素必须是<code>book</code></li>
<li><code>book</code>元素必须包含<code>name</code>，<code>author</code>等指定元素</li>
<li><code>isbn</code>元素必须包含属性<code>lang</code></li>
<li>…</li>
</ul>
<p>如何验证XML文件的正确性呢？最简单的方式是通过浏览器验证。可以直接把XML文件拖拽到浏览器窗口，如果格式错误，浏览器会报错。</p>
<p>和结构类似的HTML不同，浏览器对HTML有一定的“容错性”，缺少关闭标签也可以被解析，但XML要求严格的格式，任何没有正确嵌套的标签都会导致错误。</p>
<p>XML是一个技术体系，除了我们经常用到的XML文档本身外，XML还支持：</p>
<ul>
<li>DTD和XSD：验证XML结构和数据是否有效；</li>
<li>Namespace：XML节点和属性的名字空间；</li>
<li>XSLT：把XML转化为另一种文本；</li>
<li>XPath：一种XML节点查询语言；</li>
<li>…</li>
</ul>
<p>实际上，XML的这些相关技术实现起来非常复杂，在实际应用中很少用到，通常了解一下就可以了。</p>
<h2 id="使用DOM"><a href="#使用DOM" class="headerlink" title="使用DOM"></a>使用DOM</h2><p>因为XML是一种树形结构的文档，它有两种标准的解析API：</p>
<ul>
<li>DOM：一次性读取XML，并在内存中表示为树形结构</li>
<li>SAX：以流的形式读取XML，使用事件回调</li>
</ul>
<p>我们先来看看如何使用DOM读取XML。</p>
<p>DOM是Document Object Model的缩写，DOM模型就是把XML结构作为一个树形结构来处理，从根节点开始，每个节点都可以包含任意个子节点。</p>
<p>我们以下面的XML为例：</p>
<figure class="highlight xml hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">book</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"1"</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>Java核心技术<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">author</span>&gt;</span>Cay S. Horstmann<span class="hljs-tag">&lt;/<span class="hljs-name">author</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">isbn</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"CN"</span>&gt;</span>1234567<span class="hljs-tag">&lt;/<span class="hljs-name">isbn</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">tags</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">tag</span>&gt;</span>Java<span class="hljs-tag">&lt;/<span class="hljs-name">tag</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">tag</span>&gt;</span>Network<span class="hljs-tag">&lt;/<span class="hljs-name">tag</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">tags</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">pubDate</span>/&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">book</span>&gt;</span><br></code></pre></td></tr></tbody></table></figure>

<p>如果解析为DOM结构，它大概长这样：</p>
<figure class="highlight plaintext hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs ascii">                      ┌─────────┐<br>                      │document │<br>                      └─────────┘<br>                           │<br>                           ▼<br>                      ┌─────────┐<br>                      │  book   │<br>                      └─────────┘<br>                           │<br>     ┌──────────┬──────────┼──────────┬──────────┐<br>     ▼          ▼          ▼          ▼          ▼<br>┌─────────┐┌─────────┐┌─────────┐┌─────────┐┌─────────┐<br>│  name   ││ author  ││  isbn   ││  tags   ││ pubDate │<br>└─────────┘└─────────┘└─────────┘└─────────┘└─────────┘<br>                                      │<br>                                 ┌────┴────┐<br>                                 ▼         ▼<br>                             ┌───────┐ ┌───────┐<br>                             │  tag  │ │  tag  │<br>                             └───────┘ └───────┘<br></code></pre></td></tr></tbody></table></figure>

<p>注意到最顶层的document代表XML文档，它是真正的“根”，而<code>&lt;book&gt;</code>虽然是根元素，但它是<code>document</code>的一个子节点。</p>
<p>Java提供了DOM API来解析XML，它使用下面的对象来表示XML的内容：</p>
<ul>
<li>Document：代表整个XML文档；</li>
<li>Element：代表一个XML元素；</li>
<li>Attribute：代表一个元素的某个属性。</li>
</ul>
<p>使用DOM API解析一个XML文档的代码如下：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">InputStream input = Main.class.getResourceAsStream(<span class="hljs-string">"/book.xml"</span>);<br>DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();<br>DocumentBuilder db = dbf.newDocumentBuilder();<br>Document doc = db.parse(input);<br></code></pre></td></tr></tbody></table></figure>

<p><code>DocumentBuilder.parse()</code>用于解析一个XML，它可以接收InputStream，File或者URL，如果解析无误，我们将获得一个Document对象，这个对象代表了整个XML文档的树形结构，需要遍历以便读取指定元素的值：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">printNode</span><span class="hljs-params">(Node n, <span class="hljs-keyword">int</span> indent)</span> </span>{<br>    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; indent; i++) {<br>        System.out.print(<span class="hljs-string">' '</span>);<br>    }<br>    <span class="hljs-keyword">switch</span> (n.getNodeType()) {<br>    <span class="hljs-keyword">case</span> Node.DOCUMENT_NODE: <span class="hljs-comment">// Document节点</span><br>        System.out.println(<span class="hljs-string">"Document: "</span> + n.getNodeName());<br>        <span class="hljs-keyword">break</span>;<br>    <span class="hljs-keyword">case</span> Node.ELEMENT_NODE: <span class="hljs-comment">// 元素节点</span><br>        System.out.println(<span class="hljs-string">"Element: "</span> + n.getNodeName());<br>        <span class="hljs-keyword">break</span>;<br>    <span class="hljs-keyword">case</span> Node.TEXT_NODE: <span class="hljs-comment">// 文本</span><br>        System.out.println(<span class="hljs-string">"Text: "</span> + n.getNodeName() + <span class="hljs-string">" = "</span> + n.getNodeValue());<br>        <span class="hljs-keyword">break</span>;<br>    <span class="hljs-keyword">case</span> Node.ATTRIBUTE_NODE: <span class="hljs-comment">// 属性</span><br>        System.out.println(<span class="hljs-string">"Attr: "</span> + n.getNodeName() + <span class="hljs-string">" = "</span> + n.getNodeValue());<br>        <span class="hljs-keyword">break</span>;<br>    <span class="hljs-keyword">default</span>: <span class="hljs-comment">// 其他</span><br>        System.out.println(<span class="hljs-string">"NodeType: "</span> + n.getNodeType() + <span class="hljs-string">", NodeName: "</span> + n.getNodeName());<br>    }<br>    <span class="hljs-keyword">for</span> (Node child = n.getFirstChild(); child != <span class="hljs-keyword">null</span>; child = child.getNextSibling()) {<br>        printNode(child, indent + <span class="hljs-number">1</span>);<br>    }<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>解析结构如下：</p>
<figure class="highlight plaintext hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">Document: #document<br> Element: book<br>  Text: #text = <br>    <br>  Element: name<br>   Text: #text = Java核心技术<br>  Text: #text = <br>    <br>  Element: author<br>   Text: #text = Cay S. Horstmann<br>  Text: #text = <br>  ...<br></code></pre></td></tr></tbody></table></figure>

<p>对于DOM API解析出来的结构，我们从根节点Document出发，可以遍历所有子节点，获取所有元素、属性、文本数据，还可以包括注释，这些节点被统称为Node，每个Node都有自己的Type，根据Type来区分一个Node到底是元素，还是属性，还是文本，等等。</p>
<p>使用DOM API时，如果要读取某个元素的文本，需要访问它的Text类型的子节点，所以使用起来还是比较繁琐的。</p>
<h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p>Java提供的DOM API可以将XML解析为DOM结构，以Document对象表示；</p>
<p>DOM可在内存中完整表示XML数据结构；</p>
<p>DOM解析速度慢，内存占用大。</p>
<h2 id="使用SAX"><a href="#使用SAX" class="headerlink" title="使用SAX"></a>使用SAX</h2><p>使用DOM解析XML的优点是用起来省事，但它的主要缺点是内存占用太大。另一种解析XML的方式是SAX，SAX是Simple API for XML的缩写，它是一种基于流的解析方式，边读取XML边解析，并以时间回调的方式让调用者获取数据。因为是一边读一边解析，所以无论XML有多大，占用的内存都很小。</p>
<p>SAX解析会触发一系列事件：</p>
<ul>
<li>startDocument：开始读取XML文档；</li>
<li>startElement：读取到了一个元素，例如<code>&lt;book&gt;</code>；</li>
<li>characters：读取到了字符；</li>
<li>endElement：读取到了一个结束的元素，例如<code>&lt;/book&gt;</code>；</li>
<li>endDocument：读取XML文档结束。</li>
</ul>
<p>如果我们用SAX API解析XML，Java代码如下：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">InputStream input = Main.class.getResourceAsStream(<span class="hljs-string">"/book.xml"</span>);<br>SAXParserFactory spf = SAXParserFactory.newInstance();<br>SAXParser saxParser = spf.newSAXParser();<br>saxParser.parse(input, <span class="hljs-keyword">new</span> MyHandler());<br></code></pre></td></tr></tbody></table></figure>

<p>关键代码<code>SAXParser.parse()</code>除了需要传入一个<code>InputStream</code>外，还需要传入一个回调对象，这个对象要继承自<code>DefaultHandler</code>：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DefaultHandler</span> </span>{<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startDocument</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> SAXException </span>{<br>        print(<span class="hljs-string">"start document"</span>);<br>    }<br><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">endDocument</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> SAXException </span>{<br>        print(<span class="hljs-string">"end document"</span>);<br>    }<br><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startElement</span><span class="hljs-params">(String uri, String localName, String qName, Attributes attributes)</span> <span class="hljs-keyword">throws</span> SAXException </span>{<br>        print(<span class="hljs-string">"start element:"</span>, localName, qName);<br>    }<br><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">endElement</span><span class="hljs-params">(String uri, String localName, String qName)</span> <span class="hljs-keyword">throws</span> SAXException </span>{<br>        print(<span class="hljs-string">"end element:"</span>, localName, qName);<br>    }<br><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">characters</span><span class="hljs-params">(<span class="hljs-keyword">char</span>[] ch, <span class="hljs-keyword">int</span> start, <span class="hljs-keyword">int</span> length)</span> <span class="hljs-keyword">throws</span> SAXException </span>{<br>        print(<span class="hljs-string">"characters:"</span>, <span class="hljs-keyword">new</span> String(ch, start, length));<br>    }<br><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">error</span><span class="hljs-params">(SAXParseException e)</span> <span class="hljs-keyword">throws</span> SAXException </span>{<br>        print(<span class="hljs-string">"error:"</span>, e);<br>    }<br><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">print</span><span class="hljs-params">(Object... objs)</span> </span>{<br>        <span class="hljs-keyword">for</span> (Object obj : objs) {<br>            System.out.print(obj);<br>            System.out.print(<span class="hljs-string">" "</span>);<br>        }<br>        System.out.println();<br>    }<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>运行SAX解析代码，可以打印出下面的结果：</p>
<figure class="highlight plaintext hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">start document<br>start element:  book<br>characters:<br>     <br>start element:  name<br>characters: Java核心技术<br>end element:  name<br>characters:<br>     <br>start element:  author<br>...<br></code></pre></td></tr></tbody></table></figure>

<p>如果要读取<code>&lt;name&gt;</code>节点的文本，我们就必须在解析过程中根据<code>startElement()</code>和<code>endElement()</code>定位当前正在读取的节点，可以使用栈结构保存。每遇到一个<code>startElement()</code>入栈，每遇到一个<code>endElement()</code>出栈，这样，读到<code>characters()</code>时我们才知道读取的文本是那个节点的。可见，使用SAX API仍然比较麻烦。</p>
<h2 id="使用Jackson"><a href="#使用Jackson" class="headerlink" title="使用Jackson"></a>使用Jackson</h2><p>前面我们介绍了DOM和SAX两种解析XML的标准接口，但是，二者使用起来都不直观。</p>
<p>我们观察XML文档的内容，发现，它完全可以对应一个定义号的JavaBean中。如果能直接从XML文档解析成一个JavaBean，那比DOM和SAX不知道容易到哪里去了。</p>
<p>幸运的是，一个名叫Jackson的开源第三方库可以轻松做到XML到JavaBean的转换。我们要使用Jackson，首先添加两个Maven依赖。</p>
<ul>
<li>com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.10.1</li>
<li>org.codehaus.woodstox:woodstox-core-asl:4.4.1</li>
</ul>
<p>然后，定义好JavaBean，就可以用下面几行代码解析：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java">InputStream input = Main.class.getResourceAsStream(<span class="hljs-string">"/book.xml"</span>);<br>JacksonXmlModule <span class="hljs-keyword">module</span> = <span class="hljs-keyword">new</span> JacksonXmlModule();<br>XmlMapper mapper = <span class="hljs-keyword">new</span> XmlMapper(<span class="hljs-keyword">module</span>);<br>Book book = mapper.readValue(input, Book.class);<br>System.out.println(book.id);<br>System.out.println(book.name);<br>System.out.println(book.author);<br>System.out.println(book.isbn);<br>System.out.println(book.tags);<br>System.out.println(book.pubDate);<br></code></pre></td></tr></tbody></table></figure>

<p>注意到<code>XmlMapper</code>就是我们需要创建的核心对象，可以用<code>readValue(InputStream, Class)</code>直接读取XML并返回一个JavaBean。运行上述代码，就可以直接从Book对象中拿到数据：</p>
<figure class="highlight plaintext hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">1<br>Java核心技术<br>Cay S. Horstmann<br>1234567<br>[Java, Network]<br>null<br></code></pre></td></tr></tbody></table></figure>

<p>如果要解析的数据格式不是Jackson内置的标准格式，那么需要编写一点额外的扩展来告诉Jackson如何自定义解析，详细请看<a target="_blank" rel="noopener" href="https://github.com/FasterXML/jackson">官方文档</a>。</p>
<h2 id="使用JSON"><a href="#使用JSON" class="headerlink" title="使用JSON"></a>使用JSON</h2><p>前面我们讨论了XML这种数据格式。XML的特点是功能全面，但标签繁琐，格式复杂。在Web上使用XML现在越来越少，取而代之的是JSON这种数据结构。</p>
<p>JSON是JavaScript Object Notation的缩写，它去除了所有JavaScript执行代码，只保留JavaScript的对象格式。一个典型的JSON如下：</p>
<figure class="highlight json hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs json">{<br>    <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,<br>    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Java核心技术"</span>,<br>    <span class="hljs-attr">"author"</span>: {<br>        <span class="hljs-attr">"firstName"</span>: <span class="hljs-string">"Abc"</span>,<br>        <span class="hljs-attr">"lastName"</span>: <span class="hljs-string">"Xyz"</span><br>    },<br>    <span class="hljs-attr">"isbn"</span>: <span class="hljs-string">"1234567"</span>,<br>    <span class="hljs-attr">"tags"</span>: [<span class="hljs-string">"Java"</span>, <span class="hljs-string">"Network"</span>]<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>JSON作为数据传输格式，有几个显著的优点：</p>
<ul>
<li>JSON只允许使用UTF-8编码，不存在编码问题</li>
<li>JSON只允许使用双引号作为key，特殊字符用<code>\</code>转义，格式简单</li>
<li>浏览器内置JSON支持，如果把数据用JSON发送给浏览器，可以用JavaScript直接处理</li>
</ul>
<p>因此，JSON适合表示层次结构，因为它格式简单，仅支持以下几种数据类型：</p>
<ul>
<li>键值对：<code>{"key": value}</code></li>
<li>数组：<code>[1, 2, 3]</code></li>
<li>字符串：<code>"abc"</code></li>
<li>数值（整数和浮点数）：<code>12.34</code></li>
<li>布尔值：<code>true</code>或<code>false</code></li>
<li>空值：<code>null</code></li>
</ul>
<p>浏览器直接支持使用JavaScript对JSON进行读写：</p>
<figure class="highlight plaintext hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">// JSON string to JavaScript object:<br>jsObj = JSON.parse(jsonStr);<br><br>// JavaScript object to JSON string:<br>jsonStr = JSON.stringify(jsObj);<br></code></pre></td></tr></tbody></table></figure>

<p>所以，开发Web应用的时候，使用JSON作为数据传输，在浏览器端非常方便。因为JSON天生适合JavaScript处理，所以，绝大多数REST API都选择JSON作为数据传输格式。</p>
<p>那么，如何用Java对JSON进行读写呢？</p>
<p>常用的用于解析JSON的第三方库有：</p>
<ul>
<li>Jackson</li>
<li>Gson</li>
<li>Fastjson</li>
<li>…</li>
</ul>
<p>注意到上一节提到的那个可以解析XML的浓眉大眼的Jackson也可以解析JSON！因此我们只需要引入以下Maven依赖：</p>
<ul>
<li>com.fasterxml.jackson.core:jackson-databind:2.10.0</li>
</ul>
<p>就可以使用下面的代码解析一个JSON文件：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">InputStream input = Main.class.getResourceAsStream(<span class="hljs-string">"/book.json"</span>);<br>ObjectMapper mapper = <span class="hljs-keyword">new</span> ObjectMapper();<br><span class="hljs-comment">// 反序列化时忽略不存在的JavaBean属性:</span><br>mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, <span class="hljs-keyword">false</span>);<br>Book book = mapper.readValue(input, Book.class);<br></code></pre></td></tr></tbody></table></figure>

<p>核心代码是创建一个<code>ObjectMapper</code>对象。关闭<code>DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES</code>功能使得解析时如果JavaBean不存在该属性时解析不会报错。</p>
<p>把JSON解析为JavaBean的过程称为反序列化。如果把JavaBean变为JSON，那就是序列化。要实现JavaBean到JSON的序列化，只需要一行代码：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">String json = mapper.writeValueAsString(book);<br></code></pre></td></tr></tbody></table></figure>

<p>要把JSON的某些值解析为特定的Java对象，例如<code>LocalDate</code>，也是完全可以的。例如：</p>
<figure class="highlight json hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs json">{<br>    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Java核心技术"</span>,<br>    <span class="hljs-attr">"pubDate"</span>: <span class="hljs-string">"2016-09-01"</span><br>}<br></code></pre></td></tr></tbody></table></figure>

<p>要解析为：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span> </span>{<br>    <span class="hljs-keyword">public</span> String name;<br>    <span class="hljs-keyword">public</span> LocalDate pubDate;<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>只需要引入标准的JSR 310关于JavaTime的数据格式定义至Maven：</p>
<ul>
<li>com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.0</li>
</ul>
<p>然后，在创建<code>ObjectMapper</code>时，注册一个新的<code>JavaTimeModule</code>：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">ObjectMapper mapper = <span class="hljs-keyword">new</span> ObjectMapper().registerModule(<span class="hljs-keyword">new</span> JavaTimeModule());<br></code></pre></td></tr></tbody></table></figure>

<p>有些时候，内置的解析规则和扩展的解析规则如果都不满足我们的需求，还可以自定义解析。</p>
<p>举个例子，假设<code>Book</code>类的<code>isbn</code>是一个<code>BigInteger</code>：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span> </span>{<br>	<span class="hljs-keyword">public</span> String name;<br>	<span class="hljs-keyword">public</span> BigInteger isbn;<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>但JSON数据并不是标准的整形格式：</p>
<figure class="highlight json hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs json">{<br>    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Java核心技术"</span>,<br>    <span class="hljs-attr">"isbn"</span>: <span class="hljs-string">"978-7-111-54742-6"</span><br>}<br></code></pre></td></tr></tbody></table></figure>

<p>直接解析，肯定报错。这时，我们需要自定义一个<code>IsbnDeserializer</code>，用于解析含有非数字的字符串：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IsbnDeserializer</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">JsonDeserializer</span>&lt;<span class="hljs-title">BigInteger</span>&gt; </span>{<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> BigInteger <span class="hljs-title">deserialize</span><span class="hljs-params">(JsonParser p, DeserializationContext ctxt)</span> <span class="hljs-keyword">throws</span> IOException, JsonProcessingException </span>{<br>        <span class="hljs-comment">// 读取原始的JSON字符串内容:</span><br>        String s = p.getValueAsString();<br>        <span class="hljs-keyword">if</span> (s != <span class="hljs-keyword">null</span>) {<br>            <span class="hljs-keyword">try</span> {<br>                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> BigInteger(s.replace(<span class="hljs-string">"-"</span>, <span class="hljs-string">""</span>));<br>            } <span class="hljs-keyword">catch</span> (NumberFormatException e) {<br>                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> JsonParseException(p, s, e);<br>            }<br>        }<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;<br>    }<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>然后，在<code>Book</code>类中使用注解标注：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span> </span>{<br>    <span class="hljs-keyword">public</span> String name;<br>    <span class="hljs-comment">// 表示反序列化isbn时使用自定义的IsbnDeserializer:</span><br>    <span class="hljs-meta">@JsonDeserialize(using = IsbnDeserializer.class)</span><br>    <span class="hljs-keyword">public</span> BigInteger isbn;<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>类似的，自定义序列化时我们需要自定义一个<code>IsbnSerializer</code>，然后在<code>Book</code>类中标注<code>@JsonSerialize(using = ...)</code>即可。</p>
</body></html>
    
    </div>
    
    
    <div class="columns is-mobile is-multiline article-nav">
        <span class="column is-12-mobile is-half-desktop  article-nav-prev">
            
            <a href="/Study/Java/JDBC%E7%BC%96%E7%A8%8B/">JDBC编程</a>
            
        </span>
        <span class="column is-12-mobile is-half-desktop  article-nav-next">
            
            <a href="/Study/Java/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/">网络编程</a>
            
        </span>
    </div>
    
</article>


<div class="sharebox">
    
<div class="sharethis-inline-share-buttons"></div>
<script type='text/javascript' src='//platform-api.sharethis.com/js/sharethis.js#property=608c1408daac690012507aa2&amp;product=sop' async='async'></script>

</div>



    </div>
</section>
    <footer class="footer">
    <div class="container">
        <div class="columns content">
            <div class="column is-narrow has-text-centered">
                &copy; 2021 wanzixin&nbsp;
                Powered by <a href="http://hexo.io/" target="_blank">Hexo</a> & <a
                        target="_blank" rel="noopener" href="http://github.com/ppoffice/hexo-theme-minos">Minos</a>
            </div>
            <div class="column is-hidden-mobile"></div>

            
            <div class="column is-narrow">
                <div class="columns is-mobile is-multiline is-centered">
                
                    
                <a class="column is-narrow has-text-black" title="GitHub" target="_blank" rel="noopener" href="https://github.com/ppoffice/hexo-theme-minos">
                    
                    GitHub
                    
                </a>
                
                </div>
            </div>
            
            
<div class="column is-narrow has-text-centered">
    <div class="dropdown is-up is-right is-hoverable" style="margin-top: -0.2em;">
        <div class="dropdown-trigger">
            <button class="button is-small" aria-haspopup="true" aria-controls="dropdown-menu7">
                <span class="icon">
                    <i class="fas fa-globe"></i>
                </span>
                <span>English</span>
                <span class="icon is-small">
            <i class="fas fa-angle-down" aria-hidden="true"></i>
          </span>
            </button>
        </div>
        <div class="dropdown-menu has-text-left" role="menu">
            <div class="dropdown-content">
            
                <a href="/Study/Java/XML%E4%B8%8EJSON/" class="dropdown-item">
                    English
                </a>
            
                <a href="/zh-cn/Study/Java/XML%E4%B8%8EJSON/" class="dropdown-item">
                    简体中文
                </a>
            
            </div>
        </div>
    </div>
</div>

        </div>
    </div>
</footer>
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment-with-locales.min.js"></script>

<!-- test if the browser is outdated -->
<div id="outdated">
    <h6>Your browser is out-of-date!</h6>
    <p>Update your browser to view this website correctly. <a id="btnUpdateBrowser" target="_blank" rel="noopener" href="http://outdatedbrowser.com/">Update my browser now </a></p>
    <p class="last"><a href="#" id="btnCloseUpdateBrowser" title="Close">&times;</a></p>
</div>
<script src="//cdnjs.cloudflare.com/ajax/libs/outdated-browser/1.1.5/outdatedbrowser.min.js"></script>
<script>
    $(document).ready(function () {
        // plugin function, place inside DOM ready function
        outdatedBrowser({
            bgColor: '#f25648',
            color: '#ffffff',
            lowerThan: 'flex'
        })
    });
</script>

<script>
    window.FontAwesomeConfig = {
        searchPseudoElements: true
    }
    moment.locale("en-AU");
</script>


    
    
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-MML-AM_CHTML"></script>
<script>
    MathJax.Hub.Config({
        "HTML-CSS": {
            matchFontHeight: false
        },
        SVG: {
            matchFontHeight: false
        },
        CommonHTML: {
            matchFontHeight: false
        },
        tex2jax: {
            inlineMath: [
                ['$','$'],
                ['\\(','\\)']
            ]
        }
    });
</script>

    
    
    
    
<script src="//cdnjs.cloudflare.com/ajax/libs/lightgallery/1.6.8/js/lightgallery-all.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/justifiedGallery/3.6.5/js/jquery.justifiedGallery.min.js"></script>
<script>
    (function ($) {
        $(document).ready(function () {
            if (typeof($.fn.lightGallery) === 'function') {
                $('.article.gallery').lightGallery({ selector: '.gallery-item' });
            }
            if (typeof($.fn.justifiedGallery) === 'function') {
                $('.justified-gallery').justifiedGallery();
            }
        });
    })(jQuery);
</script>

    
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js"></script>
    <style>
        .hljs {
            position: relative;
        }

        .hljs .clipboard-btn {
            float: right;
            color: #9a9a9a;
            background: none;
            border: none;
            cursor: pointer;
        }

        .hljs .clipboard-btn:hover {
          color: #8a8a8a;
        }

        .hljs > .clipboard-btn {
            display: none;
            position: absolute;
            right: 4px;
            top: 4px;
        }

        .hljs:hover > .clipboard-btn {
            display: inline;
        }

        .hljs > figcaption > .clipboard-btn {
            margin-right: 4px;
        }
    </style>
    <script>
      $(document).ready(function () {
        $('figure.hljs').each(function(i, figure) {
          var codeId = 'code-' + i;
          var code = figure.querySelector('.code');
          var copyButton = $('<button>Copy <i class="far fa-clipboard"></i></button>');
          code.id = codeId;
          copyButton.addClass('clipboard-btn');
          copyButton.attr('data-clipboard-target-id', codeId);

          var figcaption = figure.querySelector('figcaption');

          if (figcaption) {
            figcaption.append(copyButton[0]);
          } else {
            figure.prepend(copyButton[0]);
          }
        })

        var clipboard = new ClipboardJS('.clipboard-btn', {
          target: function(trigger) {
            return document.getElementById(trigger.getAttribute('data-clipboard-target-id'));
          }
        });
        clipboard.on('success', function(e) {
          e.clearSelection();
        })
      })
    </script>

    
    

    



<script src="/js/script.js"></script>


    
    <div class="searchbox ins-search">
    <div class="searchbox-mask"></div>
    <div class="searchbox-container ins-search-container">
        <div class="searchbox-input-wrapper">
            <input type="text" class="searchbox-input ins-search-input" placeholder="Type something..." />
            <span class="searchbox-close ins-close ins-selectable"><i class="fa fa-times-circle"></i></span>
        </div>
        <div class="searchbox-result-wrapper ins-section-wrapper">
            <div class="ins-section-container"></div>
        </div>
    </div>
</div>
<script>
    (function (window) {
        var INSIGHT_CONFIG = {
            TRANSLATION: {
                POSTS: 'Posts',
                PAGES: 'Pages',
                CATEGORIES: 'Categories',
                TAGS: 'Tags',
                UNTITLED: '(Untitled)',
            },
            CONTENT_URL: '/content.json',
        };
        window.INSIGHT_CONFIG = INSIGHT_CONFIG;
    })(window);
</script>

<script src="/js/insight.js"></script>

    
</body>
</html>